'''
 * @desc : 约会配对
 * @auth : TYF
 * @date : 2019/8/26 - 23:05
'''

#3维特征:飞行里程数、玩游戏事件占比、每周冰淇淋消耗
#label:不喜欢、一般喜欢、特别喜欢

#txt样本转特征矩阵、label向量
def file_to_matrix(filename):
    fr = open(filename)
    array_lines = fr.readlines()
    number_of_lines = len(array_lines)  #一行一个样本一个n个
    return_mat = np.zeros((number_of_lines, 3)) #每个样本是三维得到  (n,3)的样本矩阵
    class_label_vector = [] #一维向量存储txt最后一列,即label
    index = 0
    for line in array_lines:
        line = line.strip()
        list_from_line = line.split('\t')
        return_mat[index, :] = list_from_line[0:3]
        class_label_vector.append(int(list_from_line[-1]))
        index += 1
    return return_mat, class_label_vector   #返回特征矩阵、label向量

#样本归一化
#每个样本3个特征,分别减去全局最小值再除以取值范围(取值范围=全局最大值-全局最小值)
def auto_norm(data_set):
    min_vals = data_set.min(0)
    max_vals = data_set.max(0)
    ranges = max_vals - min_vals
    norm_data_set = np.zeros(np.shape(data_set))
    m = data_set.shape[0]
    norm_data_set = data_set - np.tile(min_vals, (m, 1))
    norm_data_set = norm_data_set / np.tile(ranges, (m, 1))
    return norm_data_set, ranges, min_vals

#分类函数
#计算测试样本和所有训练样本的欧氏距离并排序然后投票选出最终分类结果
def classify0(input_data, data_set, labels_set, k):
    data_set_size = data_set.shape[0]
    diff_mat = np.tile(input_data, (data_set_size, 1)) - data_set   #计算欧氏距离
    sq_diff_mat = diff_mat ** 2
    sq_distances = sq_diff_mat.sum(axis=1)
    distances = sq_distances ** 0.5
    sorted_dist_indices = distances.argsort()
    class_count = {}
    for i in range(k):
        vote_index_label = labels_set[sorted_dist_indices[i]]
        class_count[vote_index_label] = class_count.get(vote_index_label, 0) + 1
        sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True) #投票法
        return sorted_class_count[0][0]

#评分函数
# 将数据集中90%用于训练,10%的数据留作测试用，挨个调用classify0统计错误率
def dating_class_test():
    ho_ratio = 0.10
    dating_data_mat, dating_labels = file_to_matrix('datingTestSet2.txt')
    norm_mat, ranges, min_vals = auto_norm(dating_data_mat)
    m = norm_mat.shape[0]
    num_test_vecs = int(m * ho_ratio)
    error_count = 0.0
    for i in range(num_test_vecs):
        classifier_result = classify0(norm_mat[i, :], norm_mat[num_test_vecs:m, :], dating_labels[num_test_vecs:m], 3)
        print("the classifier came back with: %d, the real answer is: %d" % (classifier_result, dating_labels[i]))
        if (classifier_result != dating_labels[i]): error_count += 1.0
    # 错误率
    print("the total error rate is: %f" % (error_count / float(num_test_vecs)))
